package com.chhuang.core.model;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * 
 * @author HuangChen
 *
 */
public class PersistentTools {
    /** Set logger */
    private static final Log log = LogFactory.getLog(PersistentTools.class);

    /**
     * Invoke the setter mehtod whose return type is <code>Persistable</code>
     * to retrieve its subclasses.
     *
     * @param persisUnit
     *            <code>Persistable</code>
     */
    public static void invokeSubclassesOfPersistUnit(Persistable persisUnit) {
        //Assert.notNull(persisUnit, "Perisitent unit should not be null.");
        if(null == persisUnit){
            return;
        }
        Method[] methods = persisUnit.getClass().getMethods();

        try {

            for (Method method : methods) {
                if (method.getName().startsWith("get") && !method.getName().endsWith("Class")) {
                    Class<?> returnType = method.getReturnType();
                    Type type = method.getGenericReturnType();
                    Class<?> enclosingReturnType = null;
                    /**
                     * If return as List<E>,Collection<E>,Set<E>, get the
                     * generic persistent unit type
                     */
                    if (null != type && type instanceof ParameterizedType) {
                        ParameterizedType pt = (ParameterizedType) type;
                        Type[] enclosingTypes = pt.getActualTypeArguments();
                        if (enclosingTypes != null && enclosingTypes.length == 1) {
                            enclosingReturnType = (Class<?>) enclosingTypes[0];
                        }
                        enclosingReturnType = (Class<?>) pt.getActualTypeArguments()[0];
                    }

                    if (isPersistable(returnType) || isPersistable(enclosingReturnType)) {
                        if (log.isDebugEnabled()) {
                            log.debug("Invoking the method:" + method + " ,for retrieving persistent unit<"
                                    + (enclosingReturnType != null ? enclosingReturnType : returnType) + ">");
                        }
                        /** Lazy loading subclasses */
                        if (log.isDebugEnabled()) {
                        	log.debug(method.invoke(persisUnit));
                        }else if (log.isInfoEnabled()) {
                        	log.info(method.invoke(persisUnit));
                        }else{
                            //Warnning:Log4j is not configured for com.jl.framework.dao.util.PersistentUtils
                            //Using the moethod below to loazy loading persistable model or models.
                            System.out.println(method.invoke(persisUnit));
                        }

                    }
                }
            }
        } catch (IllegalArgumentException e) {
            log.error(e.getMessage(), e);
        } catch (IllegalAccessException e) {
            log.error(e.getMessage(), e);
        } catch (InvocationTargetException e) {
            log.error(e.getMessage(), e);
        }

    }

    /**
     * @param clazz
     *            <code>Class</code>
     * @return <code>true</code> if the <code>clazz</code> is
     *         <code>Persistable</code>.
     */
    public static boolean isPersistable(Class<?> clazz) {
        if (null == clazz) {
            return false;
        }
        /** Check if the class is persistable */
        if (AbstractPersistentModel.class.getSimpleName().equals(clazz.getSimpleName())) {
            return true;
        }
        /** Since the java.lang.Object is the root class of all classes in JVM */
        if (Object.class.getSimpleName().equals(clazz.getSimpleName())) {
            return false;
        }

        return isPersistable(clazz.getSuperclass());
    }
    /**
     *
     * @param persistableCollection
     *            <code>java.util.Collection</code> such as: <code>List</code>,
     *            <code>Set</code> etc.
     */
    public static void invokeSubclassesOfCollection(Collection<? extends Persistable> persistableCollection) {
        if (persistableCollection != null) {
            for (Persistable persistable : persistableCollection) {
                invokeSubclassesOfPersistUnit(persistable);
            }
        }

    }

}
